#include "guisystem.h"

GuiSystem::GuiSystem()
{
    glfwGetWindowSize(&_wndW, &_wndH);
}

GuiSystem::~GuiSystem()
{

}

void GuiSystem::addStatic(GuiId elementId, float x, float y, float w, float h,
                          Texture& tex)
{
    GuiElement elem;
    elem._type = GET_STATIC;
    elem._enabled = true;
    elem._x = x;
    elem._y = y;
    elem._w = w;
    elem._h = h;
    elem._tex = &tex;

    _elements[elementId] = elem;
}

void GuiSystem::addButton(GuiId elementId, float x, float y, float w, float h,
                          Texture& tex)
{
    GuiElement elem;
    elem._type = GET_BUTTON;
    elem._enabled = true;
    elem._x = x;
    elem._y = y;
    elem._w = w;
    elem._h = h;
    elem._tex = &tex;

    _elements[elementId] = elem;
}

void GuiSystem::addSelectBox(GuiId elementId, float x, float y, float w,
                             float h, Texture& tex, int nOptions, int selected,
                             float realH)
{
    GuiElement elem;
    elem._type = GET_SELECTBOX;
    elem._enabled = true;
    elem._x = x;
    elem._y = y;
    elem._w = w;
    elem._h = h;
    elem._tex = &tex;
    SelectBoxExtra* extra = new SelectBoxExtra();
    extra->_nOptions = nOptions;
    extra->_selected = selected;
    if (realH == 0) {
        extra->_realH = h;
    } else {
        extra->_realH = realH;
    }
    elem._extra = extra;

    _elements[elementId] = elem;
}

float GuiSystem::widthToRel(float w)
{
    return w / _wndW;
}

float GuiSystem::heightToRel(float h)
{
    return h / _wndH;
}

bool GuiSystem::isPosInElement(float x, float y, GuiId elementId)
{
    float relX = widthToRel(x);
    float relY = heightToRel(y);
    GuiElement& elem = _elements[elementId];
    if (elem._type == GET_SELECTBOX) {

        SelectBoxExtra* extra = (SelectBoxExtra*)elem._extra;
        if (relX >= elem._x && relX <= (elem._x + elem._w) &&
                relY >= elem._y && relY <= (elem._y + extra->_realH)) {
            return true;
        }
    } else {
        if (relX >= elem._x && relX <= (elem._x + elem._w) &&
                relY >= elem._y && relY <= (elem._y + elem._h)) {
            return true;
        }
    }
    return false;
}

void GuiSystem::enable(GuiId elementId)
{
    _elements[elementId]._enabled = true;
}

void GuiSystem::disable(GuiId elementId)
{
    _elements[elementId]._enabled = false;
}

GuiEvent GuiSystem::interact(const Input& input, GuiId* elementId, int* data)
{
    for (std::map<GuiId, GuiElement>::iterator i = _elements.begin();
            i != _elements.end(); ++i) {
        GuiElement& elem = i->second;

        if (elem._enabled && elem._type != GET_STATIC) {

            switch (elem._type) {
                case GET_BUTTON:
                {
                    if (input.getCurrent().leftPressed() &&
                            !input.getPrevious().leftPressed()) {
                        if (isPosInElement(input.getCurrent().getMouseX(),
                                           input.getCurrent().getMouseY(),
                                           i->first)) {

                            *elementId = i->first;
                            return GE_CLICKED;
                        }
                    }
                    break;
                }

                case GET_SELECTBOX:
                {
                    if (input.getCurrent().leftPressed() &&
                            !input.getPrevious().leftPressed()) {
                        if (isPosInElement(input.getCurrent().getMouseX(),
                                           input.getCurrent().getMouseY(),
                                           i->first)) {

                            SelectBoxExtra* extra =
                                (SelectBoxExtra*)elem._extra;

                            float relMouse =
                                heightToRel(input.getCurrent().getMouseY()) -
                                elem._y;

                            float ry = 1.0f - relMouse / extra->_realH;
                            float rdiv = 1.0f / extra->_nOptions;

                            int final = (int)(ry / rdiv);
                            extra->_selected = final;

                            if (data != NULL) {
                                *((int*)data) = final;
                            }

                            *elementId = i->first;
                            return GE_ITEMSELECTED;
                        }
                    }
                    break;
                }

                default:
                    break;
            }
        }

    }

    return GE_NONE;
}

void GuiSystem::draw()
{
    glViewport(0, 0, _wndW, _wndH);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, 1.0, 0.0, 1.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glDisable(GL_LIGHTING);
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_TEXTURE_2D);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

    for (std::map<GuiId, GuiElement>::iterator i = _elements.begin();
            i != _elements.end(); ++i) {
        GuiElement& elem = i->second;

        if (elem._enabled) {

            if (elem._tex->hasAlpha()) {
                glEnable(GL_BLEND);
                glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                //glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
                //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
            }

            switch (elem._type) {

                case GET_STATIC:
                case GET_BUTTON:
                {
                    glBindTexture(GL_TEXTURE_2D, elem._tex->getId());
                    glBegin(GL_QUADS);
                        glTexCoord2f(0.0f, 0.0f);
                        glVertex2f(elem._x, elem._y);
                        glTexCoord2f(1.0f, 0.0f);
                        glVertex2f(elem._x + elem._w, elem._y);
                        glTexCoord2f(1.0f, 1.0f);
                        glVertex2f(elem._x + elem._w, elem._y + elem._h);
                        glTexCoord2f(0.0f, 1.0f);
                        glVertex2f(elem._x, elem._y + elem._h);
                    glEnd();
                    glBindTexture(GL_TEXTURE_2D, 0);
                    break;
                }

                case GET_SELECTBOX:
                {
                    glBindTexture(GL_TEXTURE_2D, elem._tex->getId());
                    glBegin(GL_QUADS);
                        glTexCoord2f(0.0f, 0.0f);
                        glVertex2f(elem._x, elem._y);
                        glTexCoord2f(1.0f, 0.0f);
                        glVertex2f(elem._x + elem._w, elem._y);
                        glTexCoord2f(1.0f, 1.0f);
                        glVertex2f(elem._x + elem._w, elem._y + elem._h);
                        glTexCoord2f(0.0f, 1.0f);
                        glVertex2f(elem._x, elem._y + elem._h);
                    glEnd();
                    glBindTexture(GL_TEXTURE_2D, 0);

                    /*
                    glColor3f(0.0f, 1.0f, 0.0f);
                    glBegin(GL_QUADS);
                        glVertex2f(elem._x, elem._y);
                        glVertex2f(elem._x, elem._y + elem._h);
                        glVertex2f(elem._x + elem._w, elem._y + elem._h);
                        glVertex2f(elem._x + elem._w, elem._y);
                    glEnd();
                    */

                    SelectBoxExtra* extra = (SelectBoxExtra*)elem._extra;
                    float h = extra->_realH / extra->_nOptions;
                    int sel = extra->_selected;

                    float ysub = extra->_realH - h * sel - h;

                    float padd = 0.01f;

                    glColor3f(0.3f, 0.3f, 1.0f);
                    glLineWidth(4.0f);
                    glBegin(GL_LINE_LOOP);
                        glVertex2f(elem._x + padd, elem._y + padd + ysub);
                        glVertex2f(elem._x + padd, elem._y + h - padd + ysub);
                        glVertex2f(elem._x + elem._w - padd, elem._y + h - padd + ysub);
                        glVertex2f(elem._x + elem._w - padd, elem._y + padd + ysub);

                    glEnd();

                    break;
                }
            }

            if (elem._tex->hasAlpha()) {
                glDisable(GL_BLEND);
                //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
            }

        }
    }

    glDisable(GL_BLEND);
}
